package org.skydiveapplibrary.util;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import org.skydiveapplibrary.R;
import org.skydiveapplibrary.data.LogEntryImage;
import org.skydiveapplibrary.util.DatabaseExportUtil;
import org.skydiveapplibrary.util.ImportTask.TaskProgress;

import com.dropbox.client2.DropboxAPI.Entry;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.widget.Toast;

public class ExportTask extends AsyncTask<ExportDestination, ImportTask.TaskProgress, Integer>
{
	// results types
	private static final int SUCCESS = 10;
	private static final int SUCCESS_SEND_EMAIL = 20;
	private static final int ERROR = 30;

	// export file names
	private static final String XML_EXPORT_FILE_NAME = "skydiving_logbook.xml";
	private static final String CSV_EXPORT_FILE_NAME = "skydiving_logbook.csv";
	private static final String ZIP_EXPORT_FILE_NAME = "skydiving_logbook.zip";
	// dropbox folder
	private static final String DROPBOX_FOLDER = "skydiving_logbook";

	private Activity mActivity;
	private FileManager mFileManager;
	private DatabaseExportUtil mDatabaseExporter;
	private DropBoxClient mDropBoxClient;
	private ProgressDialog mProgressDialog = null;

	public ExportTask(Activity activity)
	{
		mActivity = activity;
		mFileManager = new FileManager(activity);
		mDropBoxClient = new DropBoxClient(activity);
		mDatabaseExporter = new DatabaseExportUtil(mActivity, mFileManager);
	}
	
	@Override
	protected void onPreExecute()
	{		
		// show dialog
		// show dialog
		mProgressDialog = new ProgressDialog(new ContextThemeWrapper(mActivity, R.style.DialogStyle));
		mProgressDialog.setTitle(mActivity.getString(R.string.import_export_export_progress_message));
        mProgressDialog.setMessage("");
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setCancelable(false);
        mProgressDialog.show();
	}

	@Override
	protected Integer doInBackground(ExportDestination... destinations)
	{
		int result = SUCCESS;
		ExportDestination destination = destinations[0];
		try
		{
			if (destination == ExportDestination.Email)
			{
				result = exportToEmail();
			}
			else if (destination == ExportDestination.SDCard)
			{
				result = exportToSDCard();
			}
			else if (destination == ExportDestination.DropBox)
			{
				result = exportToDropBox();
			}
		}
		catch (Exception ex)
		{
			String message = ex.getMessage();
			message = message == null ? ex.toString() : message;
			Log.e(ExportTask.class.getCanonicalName(), message);
			result = ERROR;
		}

		return result;
	}

	@Override
	protected void onPostExecute(Integer result)
	{
		// dismiss dialog
		mProgressDialog.dismiss();

		// get resource for result
		int resourceId = (result == SUCCESS || result == SUCCESS_SEND_EMAIL) ? R.string.import_export_export_success
			: R.string.import_export_export_error;

		// show toast
		Toast status = Toast.makeText(mActivity, resourceId, Toast.LENGTH_SHORT);
		status.show();

		// send email if necessary
		if (result == SUCCESS_SEND_EMAIL)
		{
			sendEmail();
		}
	}

	private void exportDatabase() throws Exception
	{
		// xml/csv writers
		Writer xmlWriter = null;
		Writer csvWriter = null;

		try
		{
			// get writers
			xmlWriter = mFileManager.getCacheFileWriter(XML_EXPORT_FILE_NAME);
			csvWriter = mFileManager.getCacheFileWriter(CSV_EXPORT_FILE_NAME);

			// do export
			mDatabaseExporter.exportDatabase(xmlWriter, csvWriter);
		}
		finally
		{
			// close writers
			if (xmlWriter != null)
				xmlWriter.close();
			if (csvWriter != null)
				csvWriter.close();
		}
	}

	private int exportToSDCard() throws Exception
	{
		// export db to database
		publishProgress(new TaskProgress(0, mActivity.getString(R.string.import_export_exporting_log_entries)));
		exportDatabase();

		// zip files
		publishProgress(new TaskProgress(33, mActivity.getString(R.string.import_export_zipping_files)));
		zipFiles();

		// copy zip file to sd card
		publishProgress(new TaskProgress(66, mActivity.getString(R.string.import_export_copying_to_sdcard)));
		mFileManager.copyFromAppDirToSDCard(ZIP_EXPORT_FILE_NAME, true);
		
		// cleanup app files
		publishProgress(new TaskProgress(100, mActivity.getString(R.string.import_export_exporting_finished)));
		mFileManager.deleteCacheFiles();

		return SUCCESS;
	}

	private int exportToDropBox() throws Exception
	{
		publishProgress(new TaskProgress(0, mActivity.getString(R.string.import_export_exporting_log_entries)));
		
		// export db to files
		exportDatabase();
		
		publishProgress(new TaskProgress(0, mActivity.getString(R.string.import_export_uploading_log_entries)));
		
		// upload xml/csv files
		mDropBoxClient.uploadFile(
				DROPBOX_FOLDER,
				mFileManager.getCacheFile(XML_EXPORT_FILE_NAME),
				mProgressDialog);
		mDropBoxClient.uploadFile(
				DROPBOX_FOLDER,
				mFileManager.getCacheFile(CSV_EXPORT_FILE_NAME),
				mProgressDialog);
		
		Entry folderMetadata = mDropBoxClient.getFolderMetadata(DROPBOX_FOLDER);
		
		// upload images
		for (int i = 0; i < mDatabaseExporter.getExportedImageFiles().size(); i++)
		{
			LogEntryImage imageFile = mDatabaseExporter.getExportedImageFiles().get(i);
							
			publishProgress(new TaskProgress(
					0,
					MessageFormat.format(
							mActivity.getString(R.string.import_export_uploading_image),
							new Object[] { i+1, mDatabaseExporter.getExportedImageFiles().size()})));
			
			if (!mDropBoxClient.fileExists(folderMetadata, imageFile.getFilename()))
			{
				mDropBoxClient.uploadFile(DROPBOX_FOLDER, 
						mFileManager.getStorageFile(
								imageFile.getFilename()),
						mProgressDialog);
			}
		}

		// delete files
		mFileManager.deleteCacheFiles();

		return SUCCESS;
	}

	private int exportToEmail() throws Exception
	{
		// export db to files
		publishProgress(new TaskProgress(0, mActivity.getString(R.string.import_export_exporting_log_entries)));
		exportDatabase();

		// zip files
		publishProgress(new TaskProgress(50, mActivity.getString(R.string.import_export_zipping_files)));
		zipFiles();
		
		// cleanup files
		mFileManager.deleteCacheFiles();

		return SUCCESS_SEND_EMAIL;
	}

	private File zipFiles()
		throws IOException
	{
		// delete existing zip file if it exists
        File zipFile = mFileManager.getAppFile(ZIP_EXPORT_FILE_NAME);
        if (zipFile.exists())
                zipFile.delete();

        // create list of files to zip
        List<File> files = new ArrayList<File>();
        files.add(mFileManager.getCacheFile(XML_EXPORT_FILE_NAME));
        files.add(mFileManager.getCacheFile(CSV_EXPORT_FILE_NAME));
        for (LogEntryImage imageFile : mDatabaseExporter.getExportedImageFiles())
                files.add(mFileManager.getStorageFile(
                		imageFile.getFilename()));
        
        // zip
        mFileManager.zip(ZIP_EXPORT_FILE_NAME, files);
        
        return zipFile;
	}

	private void sendEmail()
	{
		// construct uri
		Uri zipUri = Uri.fromFile(mFileManager.getAppFile(ZIP_EXPORT_FILE_NAME));

		// start email activity
		Intent emailIntent = new Intent(Intent.ACTION_SEND);
		emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Skydiving Logbook");
		emailIntent.putExtra(Intent.EXTRA_TEXT, mActivity.getString(R.string.import_export_export_message));
		emailIntent.putExtra(Intent.EXTRA_STREAM, zipUri);
		emailIntent.setType("application/zip");
		mActivity.startActivity(Intent.createChooser(emailIntent, "Send mail..."));
	}
	
	protected void setProgressMessage(TaskProgress progress)
	{
		mProgressDialog.setProgress(progress.percentage);
		mProgressDialog.setMessage(progress.message);
	}
	
	@Override
	protected void onProgressUpdate(TaskProgress... progress)
	{
		setProgressMessage(progress[0]);
    }
}